package com.unswift.cloud.export;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.unswift.annotation.api.Api;
import com.unswift.annotation.api.ApiConstructor;
import com.unswift.annotation.api.ApiField;
import com.unswift.annotation.api.ApiMethod;
import com.unswift.cache.MemoryCache;
import com.unswift.cloud.adapter.logger.LoggerExportTaskAdapter;
import com.unswift.cloud.cache.CacheEnum;
import com.unswift.cloud.core.CommonOperator;
import com.unswift.cloud.pojo.bo.BaseBo;
import com.unswift.cloud.pojo.bo.logger.export.task.LoggerExportTaskQueueBo;
import com.unswift.cloud.pojo.bo.logger.export.task.LoggerExportTaskStopQueueBo;
import com.unswift.cloud.pojo.dao.logger.export.task.LoggerExportTaskDataDo;
import com.unswift.cloud.pojo.dao.logger.export.task.LoggerExportTaskUpdateDo;
import com.unswift.cloud.utils.BeanUtils;
import com.unswift.cloud.utils.ExcelUtils;
import com.unswift.exception.CoreException;
import com.unswift.utils.ClassUtils;
import com.unswift.utils.ExceptionUtils;
import com.unswift.utils.JsonUtils;
import com.unswift.utils.ObjectUtils;

@Service
@Api(value="导出逻辑业务处理类", author="unswift", date="2023-07-14", version="1.0.0")
public class ExportService extends CommonOperator{
	
	@Autowired
	@ApiField("导出任务记录表公共服务")
	private LoggerExportTaskAdapter loggerExportTaskAdapter;
	
	@ApiField("导出每次拿的数据量大小")
	@Value("${unswift.export.tempDataLimit}")
	private int tempDataLimit;
	
	@ApiField("导出最终附件上传url")
	@Value("${unswift.export.attachServerHttp}")
	private String attachServerHttp;
	
	@Autowired
	@ApiField("内存缓存")
	private MemoryCache memoryCache;

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@ApiMethod(value="导出逻辑实现", params=@ApiField("导出队列业务实体"))
	public void export(LoggerExportTaskQueueBo exportTaskBo){
		try {
			LoggerExportTaskDataDo exportTask = loggerExportTaskAdapter.findById(exportTaskBo.getId());
			int exeResult=0;
			try {
				updateStatus(exportTask.getId(), 1, exportTask.getCreateUser());
				IExportBusinessService exportService=BeanUtils.getBean(ClassUtils.forName(exportTask.getExportBeanClass()));
				BaseBo bo=JsonUtils.toJava(exportTask.getExportCondition(), ClassUtils.forName(exportTask.getExportModelClass()));
				Class<?> module=exportService.getModule();
				int count = exportService.findExportCount(bo);
				if(isStop(exportTaskBo.getId())) {
					throw ExceptionUtils.message("export.stop.task");
				}
				updateCount(exportTask.getId(), count, exportTask.getCreateUser());
				if(count>0) {
					ExportResultHandle resultHandle=new ExportResultHandle(module, exportService, exportTaskBo.getId(), exportTask.getModule(), exportTaskBo.getToken(), count, exportTask.getCreateUser(), exportTaskBo.getLanguage());
					exportService.findExportData(bo, resultHandle);
					resultHandle.finish();
				}
				updateStatus(exportTask.getId(), 2, exportTask.getCreateUser());
				exeResult=1;
			} catch (CoreException e) {
				updateError(exportTask.getId(), e.getMessage(), ExceptionUtils.getStackTrace(e), exportTask.getCreateUser());
				throw e;
			} catch (Exception e) {
				e.printStackTrace();
				updateError(exportTask.getId(), e.getMessage(), ExceptionUtils.getStackTrace(e), exportTask.getCreateUser());
				throw ExceptionUtils.exception("export.adapter.exception", e, e.getMessage());
			} finally {
				complateTask(exportTask.getId(), exeResult, exportTask.getCreateUser());
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(e.getMessage(), e);
		}
	}
	
	@ApiMethod(value="停止任务", params=@ApiField("停止导出队列业务实体"))
	public void stop(LoggerExportTaskStopQueueBo exportTaskStopBo) {
		LoggerExportTaskDataDo exportTask = loggerExportTaskAdapter.findById(exportTaskStopBo.getId());
		if(exportTask.getStatus()!=2) {//还没有结束就结束
			memoryCache.setHash(CacheEnum.EXPORT_STOP.getKey(), exportTask.getId()+"", true);
		}
	}
	
	private boolean isStop(long taskId) {
		Boolean stop=memoryCache.getHash(CacheEnum.EXPORT_STOP.getKey(), taskId+"");
		return ObjectUtils.isNotEmpty(stop) && stop;
	}
	
	@ApiMethod(value="更新任务状态", params = {@ApiField("主键"), @ApiField("状态{0:等待导出,1:导出中,2:导出完成)}"), @ApiField("操作人")})
	private void updateStatus(long id, int status, long changeUserId) {
		LoggerExportTaskUpdateDo update=new LoggerExportTaskUpdateDo();
		update.setId(id);
		update.setStatus((byte)status);
		update.setChangeUser(changeUserId);
		loggerExportTaskAdapter.update(update, false);
	}
	@ApiMethod(value="更新导出数量", params = {@ApiField("主键"), @ApiField("查询的总导出数量"), @ApiField("操作人")})
	private void updateCount(long id, long count, long changeUserId) {
		LoggerExportTaskUpdateDo update=new LoggerExportTaskUpdateDo();
		update.setId(id);
		update.setExportCount(count);
		update.setChangeUser(changeUserId);
		loggerExportTaskAdapter.update(update, false);
	}
	
	@ApiMethod(value="更新任务进度", params = {@ApiField("主键"), @ApiField("进度{0-100)}"), @ApiField("操作人")})
	private void updateProgress(long id, BigDecimal progress, long changeUserId) {
		LoggerExportTaskUpdateDo update=new LoggerExportTaskUpdateDo();
		update.setId(id);
		update.setProgress(progress);
		update.setChangeUser(changeUserId);
		loggerExportTaskAdapter.update(update, false);
	}
	
	@ApiMethod(value="更新错误信息", params = {@ApiField("主键"), @ApiField("消息"), @ApiField("堆栈消息"), @ApiField("操作人")})
	private void updateError(long id, String message, String stackMessage, long changeUserId) {
		LoggerExportTaskUpdateDo update=new LoggerExportTaskUpdateDo();
		update.setId(id);
		update.setResult((byte)0);
		if(ObjectUtils.isNotEmpty(message) && message.length()>1000) {
			message=message.substring(0, 1000);
		}
		update.setErrorMessage(message);
		update.setErrorStack(stackMessage);
		update.setChangeUser(changeUserId);
		loggerExportTaskAdapter.update(update, false);
	}
	
	@ApiMethod(value="更新任务状态", params = {@ApiField("主键"), @ApiField("状态{0:等待导出,1:导出中,2:导出完成)}"), @ApiField("操作人")})
	private void complateTask(long id, int result, long changeUserId) {
		LoggerExportTaskUpdateDo update=new LoggerExportTaskUpdateDo();
		update.setId(id);
		update.setStatus((byte)2);
		update.setProgress(new BigDecimal(100));
		update.setResult((byte)result);
		update.setEndTime(new Date());
		update.setChangeUser(changeUserId);
		loggerExportTaskAdapter.update(update, false);
	}
	
	@Api("导出大文件对象")
	@SuppressWarnings({"rawtypes", "unchecked"})
	class ExportResultHandle implements ResultHandler{

		@ApiField("工作表格对象")
		private Workbook workbook;
		
		@ApiField("工作表格sheet")
		private Sheet sheet;
		
		@ApiField("临时批量数据-负责将数据一行行收集起来，然后进行统一处理")
		private List tempDataList;
		
		@ApiField("导出实体模板")
		private Class<?> exportModuleClass;
		
		@ApiField("导出所属模块")
		private String module;
		
		@ApiField("导出excel当前行次")
		private int rowIndex;
		
		@ApiField("导出业务处理Service")
		private IExportBusinessService exportService;
		
		@ApiField("导出任务id")
		private Long taskId;
		
		@ApiField("操作人token")
		private String userToken;
		
		@ApiField("总导出数量")
		private int count;
		
		@ApiField("此任务发起人")
		private Long createUser;
		
		@ApiField("当前语言")
		private String language;
		
		@ApiConstructor(value="带参构造", params = {
			@ApiField("导出实体模板"),
			@ApiField("导出业务处理Service"),
			@ApiField("导出任务id"),
			@ApiField("导出所属模块"),
			@ApiField("操作人token"),
			@ApiField("总导出数量"),
			@ApiField("此任务发起人"),
			@ApiField("当前语言")
		})
		public ExportResultHandle(Class<?> exportModuleClass, IExportBusinessService exportService, Long taskId, String module, String userToken, int count, Long createUser, String language) {
			this.exportModuleClass=exportModuleClass;
			this.exportService=exportService;
			workbook=ExcelUtils.createBig2007Xlsx();
			sheet=ExcelUtils.createSheet(workbook, ExcelUtils.getSheetName(exportModuleClass));
			tempDataList=new ArrayList();
			this.rowIndex=0;
			this.taskId=taskId;
			this.module=module;
			this.userToken=userToken;
			this.count=count;
			this.createUser=createUser;
			this.language=language;
		}
		
		@Override
		@ApiMethod(value="mybatis大数据查询回调")
		public void handleResult(ResultContext resultContext) {
			try {
				if(isStop(taskId)) {
					throw ExceptionUtils.message("export.stop.task");
				}
				tempDataList.add(resultContext.getResultObject());
				int index = resultContext.getResultCount()-1;
				if(index!=0 && index%tempDataLimit==0) {
					exportService.rowDataHandle(tempDataList);
					rowIndex=ExcelUtils.writerData(workbook, sheet, tempDataList, exportModuleClass, rowIndex, language);
					updateProgress(taskId, new BigDecimal((rowIndex-2)*95.0/count).setScale(2, RoundingMode.HALF_UP), createUser);
					tempDataList=new ArrayList();
				}
			} catch (CoreException e) {
				throw e;
			} catch (Exception e) {
				e.printStackTrace();
				throw ExceptionUtils.exception("export.Adapter.exception", e, e.getMessage());
			}
		}
		
		public void finish() {
			if(ObjectUtils.isNotEmpty(tempDataList)) {
				exportService.rowDataHandle(tempDataList);
				ExcelUtils.writerData(workbook, sheet, tempDataList, exportModuleClass, rowIndex, language);
				updateProgress(taskId, new BigDecimal(95), createUser);
				tempDataList=null;
			}
			Map<String, String> params=new HashMap<String, String>();
			params.put("dataId", taskId+"");
			params.put("module", module);
			params.put("classify", "export");
			params.put("fileNameEncode", "true");
			params.put("saveTime", "7");
			Map<String, String> header=new HashMap<String, String>();
			header.put(tokenConfig.getKeyName(), userToken);
			Map<String, Object> response=JsonUtils.toJava(ExcelUtils.writerHttp(workbook, attachServerHttp, "file", sheet.getSheetName()+".xlsx", params, header), Map.class);
			Object code = response.get("code");
			if(ObjectUtils.isEmpty(code) || !"0".equals(code)){
				throw ExceptionUtils.message("export.adapter.exception", (String)response.get("message"));
			}
			updateProgress(taskId, new BigDecimal(100), createUser);
		}
	}
}
